package com.cragon.servlet;

import com.cragon.annotation.Controller;
import com.cragon.annotation.RequestMapping;
import com.cragon.annotation.ResponseBody;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.HashMap;
import java.util.Map;

/**
 * @Description: TODO
 * @Author: ZHOUHAILONG
 * @Date: 2020年10月13日 10:52
 */
public class MyDispatcherServlet extends HttpServlet {
	private static String XML_PATH_LOCATION = "xmlPathLocation";
	private static String PROJECT_PATH = MyDispatcherServlet.class.getResource("/").getPath();
	private static String COMPONENT_SCAN_ELEMENT = "componentScan";
	private static String PACKAGE_ATTRIBUTE = "package";
	private static Map<String, Method> methodMap = new HashMap<>();
	private static String PREFIX = "";
	private static String SUFFIX = "";

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		doPost(req, resp);
	}

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		// 拿到请求的URI
		String requestURI = req.getRequestURI();
		Method method = methodMap.get(requestURI);
		if (method != null) {
			// JDK8以前，不能直接拿方法参数名
			Parameter[] parameters = method.getParameters();
			Object[] objects = new Object[parameters.length];

			for (int i = 0; i < parameters.length; i++) {
				Parameter parameter = parameters[i];
				String name = parameter.getName();
				Class<?> type = parameter.getType();

				if (type.equals(String.class)) {
					objects[i] = req.getParameter(name);
				} else if (type.equals(HttpServletRequest.class)) {
					objects[i] = req;
				} else if (type.equals(HttpServletResponse.class)) {
					objects[i] = resp;
				} else {
					try {
						Object o = type.newInstance();
						for (Field f : type.getDeclaredFields()) {
							f.setAccessible(true);
							String fieldName = f.getName();
							f.set(o, req.getParameter(fieldName));
						}
						objects[i] = o;
					} catch (Exception e) {
						e.printStackTrace();
					}
				}

			}
			// 调用对象
			try {
				Object o = method.getDeclaringClass().newInstance();
				Object invoke = method.invoke(o, objects);
				// 判断返回值是否是void
				if (!method.getReturnType().equals(Void.class)) {
					ResponseBody annotation = method.getAnnotation(ResponseBody.class);
					if (annotation != null) {
						// 提供接口转换json
						resp.getWriter().write(String.valueOf(invoke));
					} else {
						req.getRequestDispatcher(PREFIX + String.valueOf(invoke) + SUFFIX).forward(req, resp);
					}
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		} else {
			resp.setStatus(404);
		}
	}

	@Override
	public void init(ServletConfig config) throws ServletException {
		// initParameter就是用户在web.xml中指定的配置文件地址：my-mvc.xml
		// projectPath需要进行URL转义，如：空格会变成%20，分别使用URLEncoder和URLDecoder转义

		String initParameter = config.getInitParameter(XML_PATH_LOCATION);
		// 解析XML
		File file = new File(PROJECT_PATH + "\\" + initParameter);
		Document document = parse(file);
		Element rootElement = document.getRootElement();
		Element componentScan = rootElement.element(COMPONENT_SCAN_ELEMENT);
		Element view = rootElement.element("view");
		PREFIX = view.attribute("prefix").getValue();
		SUFFIX = view.attribute("suffix").getValue();
		String packageValue = componentScan.attribute(PACKAGE_ATTRIBUTE).getValue();
		scanProjectByPath(PROJECT_PATH + "\\" + packageValue);
	}

	public void scanProjectByPath(String path) {
		File file = new File(path);
		// 递归解析项目所有文件
		scanFile(file);
	}

	public void scanFile(File file) {
		// 递归解析项目
		if (file.isDirectory()) {
			for (File f : file.listFiles()) {
				scanFile(f);
			}
		} else {
			// 如果是文件
			String filePath = file.getPath();
			String suffix = filePath.substring(filePath.lastIndexOf("."));
			if (suffix.equals(".class")) {
				String classPath = filePath.replace(new File(PROJECT_PATH).getPath() + "\\", "");
				classPath = classPath.replaceAll("\\\\", ".");
				String className = classPath.substring(0, classPath.lastIndexOf("."));
				try {
					Class<?> clazz = Class.forName(className);
					if (clazz.isAnnotationPresent(Controller.class)) {
						RequestMapping requestMapping = clazz.getAnnotation(RequestMapping.class);
						String requestMappingUrl = "";
						if (requestMapping != null) {
							requestMappingUrl = requestMapping.value();
						}

						for (Method method : clazz.getDeclaredMethods()) {
							RequestMapping annotation = method.getAnnotation(RequestMapping.class);
							if (annotation != null) {
								String methodRequestMappingUrl = "";
								methodRequestMappingUrl = annotation.value();
								System.out.println("类：" + clazz.getName() + "的" + method.getName() + "方法被映射到" + requestMappingUrl + methodRequestMappingUrl);
								methodMap.put(requestMappingUrl + methodRequestMappingUrl, method);
							}
						}
					}
				} catch (ClassNotFoundException e) {
					e.printStackTrace();
				}
			}
		}
	}

	public Document parse(File file) {
		SAXReader saxReader = new SAXReader();
		try {
			return saxReader.read(file);
		} catch (DocumentException e) {
			e.printStackTrace();
		}

		return null;
	}

}
